Completed
Push — master ( e2a49f...99a2da )
by
unknown
02:35
created

sql.js ➔ getWhereValuesToCompare   C

Complexity

Conditions 13
Paths 72

Size

Total Lines 55

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 13
nc 72
dl 0
loc 55
rs 6.7747
c 0
b 0
f 0
nop 3

1 Function

Rating   Name   Duplication   Size   Complexity  
B sql.js ➔ ... ➔ ??? 0 14 5

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like sql.js ➔ getWhereValuesToCompare often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
import {parse} from 'node-sqlparser'
2
import {Promise} from 'es6-promise'
3
import path from 'path'
4
import {
5
  coreUtils,
6
  config,
7
  Manager,
8
  getAttr
9
} from '../../'
10
11
/**
12
 * take a string and json to escape sql character and convert to sql like syntax
13
 *
14
 * Example: escapeAbeValuesFromStringRequest('select title from ./ where `abe_meta.template`=`{{article}}`', {article: "test"})
15
 *
16
 * Return string: select title from ___abe_dot______abe_dot______abe___ where  `abe_meta.template`=`test`
17
 * 
18
 * 
19
 * @param  {String} str      raw abe request sql string
20
 * @param  {Object} jsonPage json object of post
21
 * @return {String}          escaped string
22
 */
23
export function escapeAbeValuesFromStringRequest(str, jsonPage) {
0 ignored issues
show
Unused Code introduced by
The parameter jsonPage is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
24
  var matchFrom = /from .(.*?) /
25
  var matchVariable = /{{([a-zA-Z]*)}}/
26
27
  var matchFromExec = matchFrom.exec(str)
28
  if(typeof matchFromExec !== 'undefined' && matchFromExec !== null
29
    && typeof matchFromExec[1] !== 'undefined' && matchFromExec[1] !== null) {
30
31
    var fromMatch
32
    var toReplace = matchFromExec[1]
33
    while (fromMatch = matchVariable.exec(toReplace)) {
34
      try {
35
        var value = eval('jsonPage.' + fromMatch[1])
0 ignored issues
show
Security Performance introduced by
Calls to eval are slow and potentially dangerous, especially on untrusted code. Please consider whether there is another way to achieve your goal.
Loading history...
36
        if(typeof value !== 'undefined' && value !== null) {
37
          toReplace = toReplace.replace('{{' + fromMatch[1] + '}}', value)
38
        }else {
39
          toReplace = toReplace.replace('{{' + fromMatch[1] + '}}', '')
40
        }
41
      }catch(e) {
0 ignored issues
show
Coding Style Comprehensibility Best Practice introduced by
Empty catch clauses should be used with caution; consider adding a comment why this is needed.
Loading history...
42
      }
43
    }
44
45
    str = str.replace(matchFromExec[1], toReplace)
46
  }
47
48
  var from = /from ([\S\s]+)/.exec(str)
49
50
  var matches = from
51
  if(matches[1]) {
52
    var res = matches[1]
53
    var splitAttr = [' where ', ' order by ', ' limit ', ' WHERE ', ' ORDER BY ', ' LIMIT ']
54
    for(var i = 0; i < splitAttr.length; i++) {
55
      if(res.indexOf(splitAttr[i]) > -1) {
56
        res = res.substring(0, res.indexOf(splitAttr[i]))
57
      }
58
    }
59
    var escapedFrom = res.replace(/\//g, '___abe___')
60
    escapedFrom = escapedFrom.replace(/\./g, '___abe_dot___')
61
    escapedFrom = escapedFrom.replace(/-/g, '___abe_dash___')
62
    str = str.replace(res, escapedFrom)
63
  }
64
65
  str = str.replace(/``/g, '\'\'')
66
67
  return str
68
}
69
70
export function handleSqlRequest(str, jsonPage) {
71
  var req = escapeAbeValuesFromStringRequest(str, jsonPage)
72
  var request = parse(req)
73
  var reconstructSql = ''
74
75
  // SQL TYPE
76
  var type = ''
77
  if(typeof request.type !== 'undefined' && request.type !== null) {
78
    type = request.type
79
  }
80
  reconstructSql += `${type} `
81
82
  // SQL COLUMNS
83
  var columns = []
84
  if(typeof request.columns !== 'undefined' && request.columns !== null) {
85
    if(request.columns === '*') {
86
      columns.push('*')
87
    }else {
88
      Array.prototype.forEach.call(request.columns, (item) => {
89
        columns.push(item.expr.column)
90
      })
91
    }
92
  }
93
  reconstructSql += `${JSON.stringify(columns)} `
94
95
  // SQL FROM
96
  var from = []
97
  if(typeof request.from !== 'undefined' && request.from !== null) {
98
99
    Array.prototype.forEach.call(request.from, (item) => {
100
      from.push(item.table)
101
    })
102
  }else {
103
    from.push('*')
104
  }
105
  reconstructSql += `from ${JSON.stringify(from)} `
106
107
  var where
108
  if(typeof request.where !== 'undefined' && request.where !== null) {
109
    where = request.where
110
    // where = recurseWhere(request.where)
111
    // reconstructSql += 'where '
112
    // Array.prototype.forEach.call(where, (w) => {
113
    //   reconstructSql += `${w.operator} ${w.left} ${w.compare} ${w.right} `
114
    // })
115
  }
116
117
  var limit = -1
118
  if(typeof request.limit !== 'undefined' && request.limit !== null) {
119
    limit = request.limit[request.limit.length - 1].value
120
  }
121
122
  var orderby
123
  if(typeof request.orderby !== 'undefined' && request.orderby !== null && request.orderby.length > 0) {
124
    orderby = {
125
      column: request.orderby[0].expr.column,
126
      type: request.orderby[0].type
127
    }
128
    reconstructSql += `ORDER BY ${orderby.column} ${orderby.type} `
129
  }
130
131
  return {
132
    type: type,
133
    columns: columns,
134
    from: from,
135
    where: where,
0 ignored issues
show
Bug introduced by
The variable where does not seem to be initialized in case typeof request.where !=... request.where !== null on line 108 is false. Are you sure this can never be the case?
Loading history...
136
    string: reconstructSql,
137
    limit: limit,
138
    orderby: orderby
0 ignored issues
show
Bug introduced by
The variable orderby does not seem to be initialized in case typeof request.orderby ...uest.orderby.length > 0 on line 123 is false. Are you sure this can never be the case?
Loading history...
139
  }
140
}
141
142
export function getDataSource(str) {
143
  var res = str.substring(str.indexOf('source=') + 8, str.length)
144
145
  var reg = /([^'"]*=[\s\S]*?}})/g
146
  var matches = res.match(reg)
147
  if(typeof matches !== 'undefined' && matches !== null) {
148
    Array.prototype.forEach.call(matches, (match) => {
149
      res = res.replace(match, '')
150
    })
151
  }else {
152
    res = res.replace('}}', '')
153
  }
154
155
  return res.substring(0, res.length-1)
156
}
157
158
/**
159
 * replaces escaped characters with the right ones
160
 * @param  {String} statement the from clause
161
 * @return {String}           the from sanitized
162
 */
163
export function sanitizeFromStatement(statement){
164
  var from = ''
165
166
  if(typeof statement !== 'undefined' && statement !== null) {
167
    from = statement[0].replace(/___abe_dot___/g, '.')
168
    from = from.replace(/___abe___/g, '/')
169
    from = from.replace(/___abe_dash___/g, '-')
170
  }
171
172
  return from
173
}
174
175
/**
176
 * calculate the directory to analyze from the from clause
177
 * @param  {String} statement the from clause
178
 * @param  {String} tplPath   the path from the template originator
179
 * @return {string}           the directory to analyze
180
 */
181
export function getFromDirectory(statement, tplPath){
182
  var pathFromDir = ''
183
  if(typeof tplPath === 'undefined' || tplPath === null || tplPath === ''){
184
    tplPath = '/'
185
  }
186
187
  if(statement === '' || statement === '*' || statement === '/') {
188
    pathFromDir = path.join(config.root, config.data.url)
189
  }else if(statement === './') {
190
    pathFromDir = path.join(config.root, config.data.url, tplPath)
191
  }else if(statement.indexOf('/') === 0) {
192
    pathFromDir = path.join(config.root, config.data.url, statement)
193
  }else if(statement.indexOf('/') !== 0) {
194
    pathFromDir = path.join(config.root, config.data.url, tplPath, statement)
195
  }
196
197
  return pathFromDir
198
}
199
200
export function executeOrderByClause(files, orderby){
201
  if(typeof orderby !== 'undefined' && orderby !== null) {
202
    if(orderby.column.toLowerCase() === 'random') {
203
      files = coreUtils.sort.shuffle(files)
204
    }else if(orderby.column.toLowerCase() === 'date') {
205
      if(orderby.type === 'ASC') {
206
        files.sort(coreUtils.sort.byDateAsc)
207
      }else if(orderby.type === 'DESC') {
208
        files.sort(coreUtils.sort.byDateDesc)
209
      }
210
    }
211
  }
212
213
  return files
214
}
215
216
export function executeFromClause(statement, pathFromClause){
217
  var from = sanitizeFromStatement(statement)
218
219
  // if the from clause ends with a dot, we won't recurse the directory analyze
220
  if(from.slice(-1) === '.'){
221
    from = from.slice(0, -1)
222
  }
223
  
224
  var fromDirectory = getFromDirectory(from, pathFromClause)
225
226
  var list = Manager.instance.getList()
227
  var files_array = list.filter((element) => {
228
    if(element.publish) {
229
      if (element.path.indexOf(fromDirectory) > -1) {
230
        return true
231
      }
232
    }
233
    return false
234
  })
235
  return files_array
236
}
237
238
export function execQuery(pathQuery, match, jsonPage) {
239
  var res
240
  var files
241
  var request = handleSqlRequest(getAttr(match, 'source'), jsonPage)
242
243
  files = executeFromClause(request.from, pathQuery)
244
  files = executeOrderByClause(files, request.orderby)
245
  res = executeWhereClause(files, request.where, request.limit, request.columns, jsonPage)
246
247
  return res
248
}
249
250
export function executeQuerySync(pathQuerySync, match, jsonPage) {
251
  return execQuery(pathQuerySync, match, jsonPage)
252
}
253
254
export function executeQuery(pathexecuteQuery, match, jsonPage) {
255
  var p = new Promise((resolve) => {
256
    var res = execQuery(pathexecuteQuery, match, jsonPage)
257
    resolve(res)
258
  }).catch(function(e) {
259
    console.error(e)
260
  })
261
262
  return p
263
}
264
265
/**
266
 * check if a given string an url, string json, file url, abe sql request
267
 * 
268
 * get('http://google.com')
269
 * get('{"test":"test"}')
270
 * get('select * from ../')
271
 * get('test')
272
 * 
273
 * @param  {String} str 
274
 * @return {String} url | request | value | file | other
275
 */
276
export function getSourceType(str) {
277
  if(/http:\/\/|https:\/\//.test(str)) {
278
    return 'url'
279
  }
280
281
  if(/select[\S\s]*?from/.test(str)) {
282
    return 'request'
283
  }
284
285
  try {
286
    JSON.parse(str)
287
    return 'value'
288
  }catch(e) {
0 ignored issues
show
Coding Style Comprehensibility Best Practice introduced by
Empty catch clauses should be used with caution; consider adding a comment why this is needed.
Loading history...
289
290
  }
291
292
  if(/\.json/.test(str)) {
293
    return 'file'
294
  }
295
296
  return 'other'
297
}
298
299
export function executeWhereClause(files, wheres, maxLimit, columns, jsonPage){
300
  var res = []
301
  var limit = 0
302
303
  for(let file of files) {
304
    if(limit < maxLimit || maxLimit === -1) {
305
      if(typeof wheres !== 'undefined' && wheres !== null) {
306
307
        if(!recurseWhere(wheres, file.publish, jsonPage)) {
308
          var json = JSON.parse(JSON.stringify(file.publish))
309
          var jsonValues = {}
310
311
          if(typeof columns !== 'undefined' && columns !== null && columns.length > 0 && columns[0] !== '*') {
312
            
313
            Array.prototype.forEach.call(columns, (column) => {
314
              if(typeof json[column] !== 'undefined' && json[column] !== null) {
0 ignored issues
show
Bug introduced by
The variable json is changed as part of the for-each loop for example by JSON.parse(JSON.stringify(file.publish)) on line 308. Only the value of the last iteration will be visible in this function if it is called after the loop.
Loading history...
315
                jsonValues[column] = json[column]
0 ignored issues
show
Bug introduced by
The variable jsonValues is changed as part of the for-each loop for example by {} on line 309. Only the value of the last iteration will be visible in this function if it is called after the loop.
Loading history...
316
              }
317
            })
318
            jsonValues['abe_meta'] = json['abe_meta']
319
          }else {
320
            jsonValues = json
321
          }
322
323
          res.push(jsonValues)
324
          limit++
325
        }
326
      }
327
    } else {
328
      break
329
    }
330
  }
331
332
  return res
333
}
334
335
export function getWhereValuesToCompare(where, jsonDoc, jsonOriginalDoc) {
0 ignored issues
show
Unused Code introduced by
The parameter jsonOriginalDoc is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
Unused Code introduced by
The parameter jsonDoc is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
336
  var regexIsVariable = /^{{(.*)}}$/
337
  var value
338
  var compare
339
340
  try {
341
    var variableLeft = where.left.column
342
    var checkIfLeftIsAVariable = regexIsVariable.exec(variableLeft)
343
    if(typeof checkIfLeftIsAVariable !== 'undefined' && checkIfLeftIsAVariable !== null && checkIfLeftIsAVariable.length > 0) {
344
      variableLeft = checkIfLeftIsAVariable[1]
345
    }
346
    value = eval('jsonDoc.' + variableLeft)
0 ignored issues
show
Security Performance introduced by
Calls to eval are slow and potentially dangerous, especially on untrusted code. Please consider whether there is another way to achieve your goal.
Loading history...
347
  }catch(e) {
348
    // console.log('e', e)
349
  }
350
  
351
  if(where.operator === 'IN' || where.operator === 'NOT IN') {
352
    compare = []
353
    Array.prototype.forEach.call(where.right.value, (right) => {
354
      var matchRightVariable = regexIsVariable.exec(right.column)
355
      if(typeof matchRightVariable !== 'undefined' && matchRightVariable !== null && matchRightVariable.length > 0) {
356
        try {
357
          var jsonOriginalValues = eval('jsonOriginalDoc.' + matchRightVariable[1])
0 ignored issues
show
Security Performance introduced by
Calls to eval are slow and potentially dangerous, especially on untrusted code. Please consider whether there is another way to achieve your goal.
Loading history...
358
          Array.prototype.forEach.call(jsonOriginalValues, (jsonOriginalValue) => {
0 ignored issues
show
Unused Code introduced by
The parameter jsonOriginalValue is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
359
            compare.push(eval('jsonOriginalValue.' + where.left.column))
0 ignored issues
show
Security Performance introduced by
Calls to eval are slow and potentially dangerous, especially on untrusted code. Please consider whether there is another way to achieve your goal.
Loading history...
360
          })
361
        }catch(e) {}
0 ignored issues
show
Coding Style Comprehensibility Best Practice introduced by
Empty catch clauses should be used with caution; consider adding a comment why this is needed.
Loading history...
362
      }
363
      else{
364
        compare.push(right.column)
365
      }
366
    })
367
  }else {
368
    compare = where.right.column
369
    var matchRightVariable = regexIsVariable.exec(compare)
370
371
    if(typeof matchRightVariable !== 'undefined' && matchRightVariable !== null && matchRightVariable.length > 0) {
372
      try {
373
        var shouldCompare = eval('jsonOriginalDoc.' + matchRightVariable[1])
0 ignored issues
show
Security Performance introduced by
Calls to eval are slow and potentially dangerous, especially on untrusted code. Please consider whether there is another way to achieve your goal.
Loading history...
374
        if(typeof shouldCompare !== 'undefined' && shouldCompare !== null) {
375
          compare = shouldCompare
376
        }else {
377
          compare = null
378
        }
379
      }catch(e) {
380
        compare = null
381
      }
382
    }
383
  }
384
385
  return {
386
    left: value,
0 ignored issues
show
Bug introduced by
The variable value does not seem to be initialized in case var variableLeft = where.left.column on line 341 throws an error. Are you sure this can never be the case?
Loading history...
387
    right: compare
388
  }
389
}
390
391
export function recurseWhere(where, jsonDoc, jsonOriginalDoc) {
392
  var isNotLeftCorrect = false
0 ignored issues
show
Unused Code introduced by
The assignment to variable isNotLeftCorrect seems to be never used. Consider removing it.
Loading history...
393
  var isNotRightCorrect = false
0 ignored issues
show
Unused Code introduced by
The assignment to variable isNotRightCorrect seems to be never used. Consider removing it.
Loading history...
394
  var isNotCorrect = false
0 ignored issues
show
Unused Code introduced by
The assignment to variable isNotCorrect seems to be never used. Consider removing it.
Loading history...
395
  var values
396
397
  switch(where.operator) {
0 ignored issues
show
Coding Style introduced by
As per coding-style, switch statements should have a default case.
Loading history...
398
  case '=':
399
    values = getWhereValuesToCompare(where, jsonDoc, jsonOriginalDoc)
400
    isNotCorrect = !(values.left === values.right)
401
    break
402
  case '!=':
403
    values = getWhereValuesToCompare(where, jsonDoc, jsonOriginalDoc)
404
    isNotCorrect = !(values.left !== values.right)
405
    break
406
  case '>':
407
    values = getWhereValuesToCompare(where, jsonDoc, jsonOriginalDoc)
408
    isNotCorrect = !(values.left > values.right)
409
    break
410
  case '>=':
411
    values = getWhereValuesToCompare(where, jsonDoc, jsonOriginalDoc)
412
    isNotCorrect = !(values.left >= values.right)
413
    break
414
  case '<':
415
    values = getWhereValuesToCompare(where, jsonDoc, jsonOriginalDoc)
416
    isNotCorrect = !(values.left < values.right)
417
    break
418
  case '<=':
419
    values = getWhereValuesToCompare(where, jsonDoc, jsonOriginalDoc)
420
    isNotCorrect = !(values.left <= values.right)
421
    break
422
  case 'LIKE':
423
    values = getWhereValuesToCompare(where, jsonDoc, jsonOriginalDoc)
424
    isNotCorrect = !(values.left && values.left.indexOf(values.right) > -1)
425
    break
426
  case 'NOT LIKE':
427
    values = getWhereValuesToCompare(where, jsonDoc, jsonOriginalDoc)
428
    isNotCorrect = !(values.left && values.left.indexOf(values.right) === -1)
429
    break
430
  case 'AND':
431
    isNotLeftCorrect = recurseWhere(where.left, jsonDoc, jsonOriginalDoc)
432
    isNotRightCorrect = recurseWhere(where.right, jsonDoc, jsonOriginalDoc)
433
    isNotCorrect = (isNotLeftCorrect || isNotRightCorrect) ? true : false
434
    break
435
  case 'OR':
436
    isNotLeftCorrect = recurseWhere(where.left, jsonDoc, jsonOriginalDoc)
437
    isNotRightCorrect = recurseWhere(where.right, jsonDoc, jsonOriginalDoc)
438
    isNotCorrect = (isNotLeftCorrect && isNotRightCorrect) ? true : false
439
    break
440
  case 'IN':
441
    values = getWhereValuesToCompare(where, jsonDoc, jsonOriginalDoc)
442
    isNotCorrect = true
443
    Array.prototype.forEach.call(values.right, (right) => {
444
      if(values.left === right) {
445
        isNotCorrect = false
446
      }
447
    })
448
    break
449
  case 'NOT IN':
450
    values = getWhereValuesToCompare(where, jsonDoc, jsonOriginalDoc)
451
    isNotCorrect = false
452
    Array.prototype.forEach.call(values.right, (right) => {
453
      if(values.left === right) {
454
        isNotCorrect = true
455
      }
456
    })
457
    break
458
  }
459
  return isNotCorrect
460
}